﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace DDD.Core {

    /// <summary>
    /// <c>Uniform</c> 変数をあらわす構造体
    /// </summary>
    /// <remarks>
    /// この構造体はGPU側からシェーダーに渡す <c>Uniform</c> 変数(シェーダーのグローバル変数のようなもの)をラップしたものです。
    /// 変数名<see cref="Name"/> と､転送するデータの元になるソース オブジェクト <see cref="Object"/> と
    /// そのプロパティ情報 <see cref="PropertyInfo"/> の3つの組みを保存します。
    /// プロパティはシェーダー側で宣言された型と互換性のある型である必要があります｡
    /// ユーザーが<see cref="UnfiormVariable"/> 型のオブジェクトを直接インスタンス化する事はできません｡
    /// 必ずエンジン側が作成したものを戻り値で受け取ります。
    /// この構造体はCPU側からシェーダーに転送するデータをラップしたものであり､
    /// シェーダー側で宣言された変数については <see cref="ShaderDeclaration"/> 構造体が保存します｡
    /// シェーダー プログラムの詳細については <see cref="ShaderProgram"/> を参照してください。
    /// <para>
    /// <h3>互換性のある型</h3>
    /// GPUで宣言された型と､CPU側のプロパティの型は一致するか互換性がある型である必要があります｡
    /// 互換性のある型では常識的な暗黙の型変換が行われる場合があります｡
    /// 以下の表は GLSL の基本型(1列目)と､それと一致する DDD の型(2列目)を示す表です｡
    /// ここに乗っていない GLSL の基本型(例えばdouble)には対応していません｡
    /// GLSL の型の詳細については GLSL の仕様書を参照してください｡
    /// </para>
    /// <list type="table">
    ///   <listheader>
    ///     <term>GLSL基本型</term>
    ///     <term>対応するCPU側の型</term>
    ///   </listheader>
    ///   <item>
    ///     <description>bool</description>
    ///     <description>bool</description>
    ///   </item>
    ///   <item>
    ///     <description>int</description>
    ///     <description>int</description>
    ///   </item>
    ///   <item>
    ///     <description>uint</description>
    ///     <description>uint</description>
    ///   </item>
    ///   <item>
    ///     <description>float</description>
    ///     <description>float</description>
    ///   </item>
    ///   <item>
    ///     <description>vec2</description>
    ///     <description><see cref="Vector2{float}"/></description>
    ///   </item>
    ///   <item>
    ///     <description>vec3</description>
    ///     <description><see cref="Vecto3{float}"/></description>
    ///   </item>
    ///   <item>
    ///     <description>vec4</description>
    ///     <description><see cref="Vector4{float}"/></description>
    ///   </item>
    ///   <item>
    ///     <description>bvec2</description>
    ///     <description><see cref="Vector2{bool}"/></description>
    ///   </item>
    ///   <item>
    ///     <description>bvec3</description>
    ///     <description><see cref="Vector3{bool}"/></description>
    ///   </item>
    ///   <item>
    ///     <description>bvec4</description>
    ///     <description><see cref="Vector4{bool}"/></description>
    ///   </item>
    ///   <item>
    ///     <description>ivec2</description>
    ///     <description><see cref="Vector2{int}"/></description>
    ///   </item>
    ///   <item>
    ///     <description>ivec3</description>
    ///     <description><see cref="Vector3{int}"/></description>
    ///   </item>
    ///   <item>
    ///     <description>ivec4</description>
    ///     <description><see cref="Vector4{int}"/></description>
    ///   </item>
    ///   <item>
    ///     <description>uvec2</description>
    ///     <description><see cref="Vector2{uint}"/></description>
    ///   </item>
    ///   <item>
    ///     <description>uvec3</description>
    ///     <description><see cref="Vector3{uint}"/></description>
    ///   </item>
    ///   <item>
    ///     <description>uvec4</description>
    ///     <description><see cref="Vector4{uint}"/></description>
    ///   </item>
    ///   <item>
    ///     <description>mat2x2(mat2)</description>
    ///     <description><see cref="Matrix2x2"/></description>
    ///   </item>
    ///   <item>
    ///     <description>mat3x3(mat3)</description>
    ///     <description><see cref="Matrix3x3"/></description>
    ///   </item>
    ///   <item>
    ///     <description>mat4x4(mat4)</description>
    ///     <description><see cref="Matrix4x4"/></description>
    ///   </item>
    ///   <item>
    ///     <description>Sampler2D</description>
    ///     <description><see cref="Sampler"/></description>
    ///   </item>
    ///   <item>
    ///     <description>SamplerCube</description>
    ///     <description><see cref="Sampler"/></description>
    ///   </item>
    ///   <item>
    ///     <description>Sampler2DSahdow</description>
    ///     <description><see cref="Sampler"/></description>
    ///   </item>
    ///   <item>
    ///     <description>iSampler2D</description>
    ///     <description><see cref="Sampler"/></description>
    ///   </item>
    ///   <item>
    ///     <description>iSampler2D</description>
    ///     <description><see cref="Sampler"/></description>
    ///   </item>
    ///   <item>
    ///     <description>uSampler2D</description>
    ///     <description><see cref="Sampler"/></description>
    ///   </item>
    ///   <item>
    ///     <description>uSampler2D</description>
    ///     <description><see cref="Sampler"/></description>
    ///   </item>
    /// </list>
    /// <para>
    /// <h3>構造体</h3>
    /// シェーダーで定義された構造体は､構造体名とそのメンバー名をピリオド(.)で結んだものが変数名になります｡
    /// 例えば､
    /// </para>
    /// <code>
    /// strcut Light {
    ///   float intensity;
    ///   vec3  position;
    /// };
    /// uniform Light light;
    /// </code>
    /// <para>
    /// は､ <c>float light.intensity</c> と <c>vec3 light.position</c> の2つの変数を宣言したのと等価です｡
    /// </para>
    /// <para>
    /// <h3>配列</h3>
    /// シェーダーで宣言された配列は､そのままCPU側でも配列が対応します｡
    /// <code>
    /// uniform float color[3];
    /// </code>
    /// は､CPU側では､<c>float[]</c> に相当します｡
    /// ここで <c><see cref="Vector3{float}"/></c> に相当しない事に注意してください｡
    /// シェーダー側が配列ならC#側も配列である必要があります｡
    /// </para>
    /// <note type="implement">
    /// 現在の実装はこの仕様とは異なり上記の配列に､<see cref="Vector3{float}"/> をアサインしても動作する｡
    /// これは実装する時に勘違いしたものであり､次のバージョンで仕様通りに例外を発生するように修正する｡
    /// </note>
    /// <para>
    /// <h3>プロパティの評価のタイミング</h3>
    /// アサインしたプロパティは <see cref="Attachable.Reflesh"/> を呼ぶまで評価されません(遅延評価)｡
    /// これはレンダリングとデータの更新を別の頻度で行えるようにデザインしているからです｡
    /// </para>
    /// <note type="implement">
    /// 現在の実装はこの仕様とは異なりレンダリングのタイミングでプロパティの値を評価している｡
    /// (<see cref="Attachable.Reflesh"/> は何もしない)
    /// これは明確な実装ミスであり､将来のバージョンで修正される｡
    /// </note>
    /// </remarks>
    /// <seealso cref="ShaderProgram"/>
    /// <seealso cref="AttributeVariable"/>
    /// <seealso cref="ShaderDeclaration"/>
    public struct UniformVariable {
        /// <summary>
        /// コンストラクター
        /// </summary>
        /// <param name="name"><c>Uniform</c> 変数名</param>
        /// <param name="src">ソース オブジェクト</param>
        /// <param name="propertyName">プロパティ名</param>
        internal UniformVariable (string name, object src, string propertyName)
            : this () {
            this.Name = name;
            this.Source = src;
            this.PropertyName = propertyName;
        }

        /// <summary>
        /// <c>Uniform</c> 変数名
        /// </summary>
        /// <remarks>
        /// データ転送先となる<c>Uniform</c> 変数の名前です｡
        /// </remarks>
        public string Name { get; private set; }

        /// <summary>
        /// ソース オブジェクト
        /// </summary>
        /// <remarks>
        /// データの転送元になるソース オブジェクトです｡
        /// </remarks>
        public object Source { get; private set; }

        /// <summary>
        /// プロパティ名
        /// </summary>
        /// <remarks>
        /// データの転送元になるプロパティの名前です｡
        /// </remarks>
        public string PropertyName { get; private set; }

        /// <inheritdoc/>
        public override string ToString () {
            return string.Format("Name={0}", Name);
        }
    }
}
